package game;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swiftpass.config.SwiftpassConfig;

import game.common.StringConstants;
import game.db.DataSourceManager;
import game.http.AgentHttpHandler;
import game.http.ScriptRunHandler;
import game.http.StatHandler;
import game.http.SwiftpassHttpHandler;
import game.lan.HallLanClientManager;
import game.lan.HallLanServerManager;
import game.module.announce.dao.AnnounceCache;
import game.module.club.dao.ClubCache;
import game.module.hall.dao.OnlineCountCache;
import game.module.offline.logic.PlayerOfflineManager;
import game.module.quartz.QuartzSchedulerManager;
import game.session.CharacterDownlineManager;
import game.session.GlobalTimer;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.netty.util.Timeout;
import lion.common.MsgCodeAnn;
import lion.common.NamedThreadFactory;
import lion.common.Reloadable;
import lion.common.ScheduledThreadPoolExecutorLog;
import lion.http.HttpMapUri;
import lion.http.Netty4HttpServer;
import lion.lua.LuaServer;
import lion.netty4.core.BaseIoExecutor;
import lion.netty4.core.BaseProtoIoExecutor;
import lion.netty4.core.MyIoExecutor;
import lion.netty4.core.ServerStat;
import lion.netty4.filter.MsgAccessLimitCache;
import lion.netty4.message.GamePlayer;
import lion.netty4.message.IGameServer;
import lion.netty4.message.MsgDispatcher;
import lion.netty4.message.MyRequestMessage;
import lion.netty4.message.RequestMessageRaw;
import lion.netty4.message.RequestProtoMessage;
import lion.netty4.processor.MsgProcessor;

public class HallServer implements IGameServer {

	private static Logger logger = LoggerFactory.getLogger(HallServer.class);

	public static MsgDispatcher<MsgProcessor> msgDispatcher = new MsgDispatcher<MsgProcessor>();

	// private DisruptorExecutor serialExecuter;

	private ServerConfig serverConfig;

	private LuaServer netty4Server;

	private Netty4HttpServer netty4HttpServer;

	private Thread shutdownHook;

	/** 数据库操作使用的线程池 */
	public static ScheduledExecutorService executorService;

	private static HallServer instance;

	private HallLanClientManager lanClientManager;

	private Map<Integer, Timeout> timeouts = new HashMap<Integer, Timeout>();

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		new HallServer().start();
	}

	public static HallServer getInstance() {
		return instance;
	}

	private void start() throws Exception {
		logger.warn("hall server start!");
		instance = this;
		long startTime = System.currentTimeMillis();
		serverConfig = loadConfig();
		DataSourceManager.init(serverConfig.getMysqlUrl(), serverConfig.getMysqlClassname(),
				serverConfig.getMysqlUsername(), serverConfig.getMysqlPassword());
		initExecuter();
		registMessageProcessor();
		loadTemplates();
		BaseIoExecutor.setGameServer(this);
		BaseProtoIoExecutor.setGameServer(this);
		MyIoExecutor.setGameServer(this);
		logger.warn("start netty server");
		netty4Server = new LuaServer(serverConfig.getSocketPort());
		netty4Server.run();
		initHttpServer();
		initQuartzJobs();
		// init bad word
		// logger.warn("init bad word!");
		// List<BadWordTemplate> badWordTemplates =
		// BadWordTemplateCache.getInstance().loadFromDb();
		// BadWordFilter.getInstance().reload(badWordTemplates);
		// init lan server
		HallLanServerManager.getInstance().startServer(serverConfig.getHsLanPort());
		// init lan client
		lanClientManager = new HallLanClientManager();
		// player offline cache
		logger.warn("init player offline cache!");
		PlayerOfflineManager.getInstance().init();
		logger.warn("init announce!");
		AnnounceCache.getInstance().loadFromDb();
		logger.warn("init rank!");
		// RankCache.getInstance().load();
		logger.warn("start log Stage");
		// LogStageManager.getInstance().timingUpdateLog();
		logger.warn("start log stageCache");
		// LogStageCache.getInstance().loadFromDb();
		logger.warn("load Invite");
		// ActivityCache.getInstance().loadFromDb();
		logger.warn("start activity timer");
		// ActivityManager.getInstance().AnswerTimeEndTimer();
		// ActivityManager.getInstance().AnswerDailyTimeEndTimer();
		// ActivityManager.getInstance().rechargeTimeEndTimer();
		// ActivityManager.getInstance().consumptionTimeEndTimer();
		// ActivityManager.getInstance().kaiFuJinSaiTimer();
		// PaymentManager.getInstance().loadPayPlayers();
		logger.warn("schedule query room online size!");
		OnlineCountCache.getInstance().scheduleQueryOnlineCount();
		// 加载好友上线
		// FriendCache.getInstance().loadFromDb();
		// ActivityQZYBCache.getInstance().loadFromDb();
		// ActivityChongZhiBangCache.getInstance().loadFromDb();
		// ActivityXiaoFeiBangBangCache.getInstance().loadFromDb();

		// logger.warn("player offline cache size={}",
		// PlayerOfflineManager.getInstance().getPlayerOfflineCacheSize());
		
		//加载俱乐部数据
		ClubCache.getInstance().loadFromDb();
		
		addShutDownHook();
		// online stat
		// OnlineUserManager.getInstance().run();
		// 诸神争霸匹配
		long endTime = System.currentTimeMillis();
		logger.warn("hall server startup success! in {} s", (endTime - startTime) / 1000);
		ServerStat.startUpTime = endTime;
	}

	private void initQuartzJobs() {
		logger.warn("init quartz jobs!");
		QuartzSchedulerManager.getInstance();
	}

	/**
	 * 加载模版表
	 */
	public void loadTemplates() {
		logger.warn("load templates");
		// ItemTemplateCache.getInstance().reload();
		// UserLevelTemplateCache.getInstance().reload();
		// load language template
		FastClasspathScanner fastClasspathScanner = new FastClasspathScanner("game.module");
		List<String> list1 = fastClasspathScanner.scan().getNamesOfClassesImplementing(Reloadable.class);
		try {
			for (String processorClassName : list1) {
				Class<?> class1 = Class.forName(processorClassName);
				Method getInstanceMethod = class1.getMethod("getInstance");
				Reloadable aInstance = (Reloadable) getInstanceMethod.invoke(null);
				logger.warn("load template={}", class1);
				aInstance.reload();

			}
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	public void registMessageProcessor() {
		// 注册消息
		logger.warn("regist message processors!");
		Map<Integer, Integer> accessIntervalLimitMap = new HashMap<Integer, Integer>();
		FastClasspathScanner fastClasspathScanner = new FastClasspathScanner("game.module");
		List<String> list1 = fastClasspathScanner.scan().getNamesOfClassesWithAnnotation(MsgCodeAnn.class);
		try {
			for (String processorClassName : list1) {
				Class<?> processorClass = Class.forName(processorClassName);
				MsgCodeAnn ann = processorClass.getAnnotation(MsgCodeAnn.class);
				Object msgProcessorObj = processorClass.newInstance();
				if (msgDispatcher.containsMsgcode(ann.msgcode())) {
					logger.error("duplicate msgcode={}", ann.msgcode());
				}
				msgDispatcher.addMsgProcessor(ann.msgcode(), (MsgProcessor) msgProcessorObj);
				accessIntervalLimitMap.put(ann.msgcode(), ann.accessLimit());
			}
		} catch (Exception e) {
			logger.error("", e);
		}
		// 注册IO消息访问频率控制
		MsgAccessLimitCache.getInstance().initMsgTimeTemplate(accessIntervalLimitMap);
		// 打印processor信息
		// msgDispatcher.dumpProcessors();
	}

	@Override
	public boolean checkIP(String address) {
		return true;
	}

	public void initExecuter() {
		logger.warn("init executer");
		// serialExecuter = new DisruptorExecutor();
		// serialExecuter.startService();
		// 初始化线程池
		executorService = new ScheduledThreadPoolExecutorLog(Runtime.getRuntime().availableProcessors() * 4,
				new NamedThreadFactory("executorPool"));
	}

	public void shutdown() {
		logger.warn("shutting down hall server!");
		netty4Server.shutdown();
		logger.warn("close lan server!");
		HallLanServerManager.getInstance().shutdown();
		// close timer
		logger.warn("global timmer shutdown!");
		GlobalTimer.getInstance().shutdown();
		logger.warn("close http server!");
		netty4HttpServer.shutdown();
		// 逻辑处理
		shutdownGameLogic();
		// logger.warn("serial executer shutdown!");
		// serialExecuter.shutdown();
		logger.warn("begin threadpool shutdown!");
		try {
			executorService.shutdownNow();
			// executorService.shutdown();
			while (!executorService.isTerminated()) {
				logger.warn("thread pool info:{}", executorService);
				executorService.awaitTermination(5, TimeUnit.SECONDS);
			}
			logger.warn("logic threadpool terminate successfully!");
		} catch (Exception e) {
			logger.error("", e);
		}
		// logger.warn("close player offline cache!");
		// PlayerOfflineManager.getInstance().clear();
		logger.warn("close quartz scheduler!");
		QuartzSchedulerManager.getInstance().shutdown();
		// close db
		DataSourceManager.getInstance().shutdown();
		logger.warn("successfully close hall server!");

	}

	private void shutdownGameLogic() {
		logger.warn("arena save to db!");
		// ArenaManager.getInstance().saveToDb();
		// ActivityQZYBCache.getInstance().save2Db();
		// ActivityChongZhiBangCache.getInstance().save2Db();
		// ActivityXiaoFeiBangBangCache.getInstance().save2Db();
		// 保存玩家数据
		logger.warn("save all player data!");
		CharacterDownlineManager.getInstance().serverShutdown();

		// 保存日志
		updateLog();
	}

	/**
	 * 更新日志
	 */
	private void updateLog() {
		// 更新副本失败次数日志
		// LogStageCache cache = LogStageCache.getInstance();
		// Collection<LogStageFail> logStageFails =
		// cache.getUpdateStageFailLogs();
		// if (logStageFails != null && !logStageFails.isEmpty()) {
		// LogStageDao.getInstance().updateStageFailLog(logStageFails);
		// }
	}

	public ServerConfig loadConfig() {
		logger.warn("load executer");
		InputStream propStream = null;
		Properties properties = new Properties();
		String serverConfStr = ServerConfig.CONFIG_FILE_NAME;
		try {
			propStream = HallServer.class.getClassLoader().getResourceAsStream(serverConfStr);
			if (propStream == null) {
				propStream = HallServer.class.getClassLoader().getResourceAsStream("/" + serverConfStr);
				properties.load(propStream);
			} else {
				properties.load(propStream);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		ServerConfig serverConfig = new ServerConfig();
		serverConfig.setSocketPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_SOCKET_PORT)));
		serverConfig.setHttpPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_HTTP_PORT)));
		serverConfig.setHsLanPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_HS_LAN_PORT)));
		serverConfig.setZoneId(Integer.valueOf(properties.getProperty(ServerConfig.KEY_ZONE_ID)));
		serverConfig.setZoneName(properties.getProperty(ServerConfig.KEY_ZONE_NAME));
		serverConfig.setIsAndroid(Boolean.valueOf(properties.getProperty(ServerConfig.KEY_IS_ANDROID)));
		serverConfig.setMysqlUrl(properties.getProperty(ServerConfig.KEY_MYSQL_URL));
		serverConfig.setMysqlClassname(properties.getProperty(ServerConfig.KEY_MYSQL_CLASSNAME));
		serverConfig.setMysqlUsername(properties.getProperty(ServerConfig.KEY_MYSQL_USERNAME));
		serverConfig.setMysqlPassword(properties.getProperty(ServerConfig.KEY_MYSQL_PASSWORD));
		serverConfig.setCrossHost(properties.getProperty(ServerConfig.KEY_CROSS_HOST));
		serverConfig.setCrossLanPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_CROSS_LAN_PORT)));
		serverConfig.setGmEnable(Boolean.valueOf(properties.getProperty(ServerConfig.KEY_GM_ENABLE)));
		String lanAllowIps = properties.getProperty(ServerConfig.KEY_LAN_ALLOW_IP);
		String[] allowIps = StringUtils.split(lanAllowIps, StringConstants.SEPARATOR_DI);
		Set<String> allowIpSet = new HashSet<>();
		for (String aIp : allowIps) {
			allowIpSet.add(aIp);
		}
		serverConfig.setLanAllowIps(allowIpSet);
		serverConfig.setIosIapIsSandbox(Boolean.valueOf(properties.getProperty(ServerConfig.KEY_IOS_IAP_SANDBOX)));
		serverConfig.setDalanPayCallback(properties.getProperty(ServerConfig.KEY_DALAN_PAY_CALLBACK));
		serverConfig.setRsSocketHost(properties.getProperty(ServerConfig.KEY_RS_SOCKET_HOST));
		serverConfig.setRsSocketPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_RS_SOCKET_PORT)));
		serverConfig.setRsLanHost(properties.getProperty(ServerConfig.KEY_RS_LAN_HOST));
		serverConfig.setRsLanPort(Integer.valueOf(properties.getProperty(ServerConfig.KEY_RS_LAN_PORT)));
		serverConfig.setAgentServerUrl(properties.getProperty("agent.server.url"));
		// 支付配置
		String payConfigFile = "swiftpass.properties";
		InputStream pay_propStream = null;
		Properties pay_properties = new Properties();
		try {
			pay_propStream = HallServer.class.getClassLoader().getResourceAsStream(payConfigFile);
			if (pay_propStream == null) {
				pay_propStream = HallServer.class.getClassLoader().getResourceAsStream("/" + payConfigFile);
				pay_properties.load(pay_propStream);
			} else {
				pay_properties.load(pay_propStream);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		SwiftpassConfig.key = pay_properties.getProperty("key");
		SwiftpassConfig.mch_id = pay_properties.getProperty("mch_id");
		SwiftpassConfig.req_url = pay_properties.getProperty("req_url");
		SwiftpassConfig.notify_url = pay_properties.getProperty("notify_url");
		return serverConfig;
	}

	public void initHttpServer() {
		try {
			logger.warn("init http server,port={}!", serverConfig.getHttpPort());
			netty4HttpServer = new Netty4HttpServer(serverConfig.getHttpPort());
			HttpMapUri mapHanlder = new HttpMapUri(new ScriptRunHandler("/script"), new StatHandler("/stat"),
					new AgentHttpHandler("/agent"),new SwiftpassHttpHandler("/swiftpass"));
			netty4HttpServer.start(null, mapHanlder);
			// // use default backlog value
			// httpServer = HttpServer.create(new
			// InetSocketAddress(serverConfig.getHttpPort()), 0);
			// // httpServer.createContext("/console", new ConsoleHandler());
			// httpServer.createContext("/script", new ScriptRunHandler());
			// httpServer.createContext("/stat", new StatHandler());
			// httpServer.createContext("/config", new ConfigHandler());
			// // httpServer.createContext("/download", new DownloadHandler());
			// httpServer.setExecutor(null); // creates a default executor
			// httpServer.start();
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	@Override
	public MsgProcessor getMsgProcessor(int msgCode) {
		return msgDispatcher.getMsgProcessor(msgCode);
	}

	@Override
	public void syncExecuteIoRequest(GamePlayer player, RequestMessageRaw requestMsg) {
		// serialExecuter.submitIoTask(player, requestMsg);
	}

	@Override
	public void syncExecuteIoRequest(GamePlayer player, RequestProtoMessage requestMsg) {
		// serialExecuter.submitProtoTask(player, requestMsg);
	}

	@Override
	public void syncExecuteIoRequest(GamePlayer player, MyRequestMessage requestMsg) {

	}

	public void syncExecuteRunnableTask(Runnable runnableTask) {
		// serialExecuter.submitRunnableTask(null, runnableTask);
	}

	private void addShutDownHook() {
		shutdownHook = new Thread("shutdown-hook") {
			public void run() {
				try {
					shutdown();
				} catch (Exception e) {
					logger.error("", e);
				}
			}
		};
		Runtime.getRuntime().addShutdownHook(shutdownHook);
	}

	public void removeShutDownHook() {
		if (shutdownHook != null) {
			Runtime.getRuntime().removeShutdownHook(shutdownHook);
		}
	}

	public void scriptTest() {
		logger.info("i am in script test!");
	}

	public ServerConfig getServerConfig() {
		return serverConfig;
	}

	public Timeout getTimeouts(int type) {
		return timeouts.get(type);
	}

	public void removeTimeout(int type) {
		timeouts.remove(type);
	}

	public void setTimeouts(int type, Timeout timeout) {
		if (timeout != null) {
			timeouts.put(type, timeout);
		}
	}

	public HallLanClientManager getLanClientManager() {
		return lanClientManager;
	}

}
